package com.kude.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;

import com.kude.HibernateSessionFactory;


public abstract class BaseDAO<T, ID_TYPE extends Serializable> implements
		IBaseDAO, java.io.Serializable {
	
	private Class<T> clazz;

	/**
	 * constructor 2 with argument
	 * 
	 * @param Class <T> c
	 * */
	protected BaseDAO(Class<T> c) {
		this.clazz = c;
	}

	/** get the class */
	protected Class<T> getClazz() {
		return this.clazz;
	}

	/** get session */
	public final Session getSession() {
		return HibernateSessionFactory.getSession();
	}

	/**
	 * save transient object with hibernate method
	 * 
	 * @param T t
	 * */
	public void save(T t) {
		Transaction tx = this.getSession().beginTransaction();
		tx.begin();
		this.getSession().save(t);
		tx.commit();
	}

	/**
	 * method to delete an object from database, this method really delete data
	 * from database unless you replace it focus: you should make sure that the
	 * object has an ID
	 * 
	 * @param T t
	 * */
	public void delete(T t) {
		this.getSession().delete(t);
	}
	
	/**
	 * delete the object by the sql manner
	 * @param sql
	 */
	public void delete(String sql) {
		this.getSession().createQuery(sql).executeUpdate();
	}

	/**
	 * method to delete an object by ID, this method really delete data from
	 * database unless you replace it
	 * 
	 * @param ID_TYPE id
	 * */
	public void deleteById(ID_TYPE id) {
		Transaction tx = this.getSession().beginTransaction();
		tx.begin();
		this.getSession().delete(this.getById(id));
		tx.commit();
	}

	/**
	 * update an object which had already been saved in database you should make
	 * sure that the object has an ID
	 * 
	 * @param T t
	 * */
	public void update(T t) {
		Transaction tx = this.getSession().beginTransaction();
		tx.begin();
		this.getSession().update(t);
		tx.commit();
	}

	/**
	 * save or update an object
	 * 
	 * @param T t
	 * */
	public void saveOrUpdate(T t) {
		this.getSession().saveOrUpdate(t);
	}

	/**
	 * find an object throught id
	 * 
	 * @param ID_TYPE id
	 * */
	public T getById(ID_TYPE id) {
		T pojo = (T) this.getSession().get(clazz, id);
		return pojo;
	}

	/**
	 * get the Criteria
	 * */
	public Criteria getCriteria() {
		Criteria crit = getSession().createCriteria(getClazz());
		return crit;
	}
	
	/**
	 * find a list of objects from the start row with the number of count
	 * 
	 * @param int first, int count
	 * */
	public List<T> findByCriteria(int offset, int limit) {
		Criteria crit = getSession().createCriteria(getClazz());
		crit.setFirstResult(offset);
		crit.setMaxResults(limit);
		return crit.list();
	}

	/**
	 * find a list of objects order by Order
	 * 
	 * @param Order order
	 * */
	public List<T> findByCriteria(Order order,int offset,int limit) {
		Criteria crit = getSession().createCriteria(getClazz());
		if (order != null) {
			crit.addOrder(order);
		}
		crit.setFirstResult(offset);
		crit.setMaxResults(limit);
		return crit.list();
	}
	
	/**
	 * find a list of objects by Criteria or Criteria list
	 * 
	 * @param Criterion ... criterion
	 * */
	public List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getClazz());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}

	/**
	 * find a list of objects by Criteria or Criteria list and set the start row
	 * and items size
	 * 
	 * @param int first, int count, Criterion... criterion
	 * */
	public List<T> findByCriteria(int first, int count, Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getClazz());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		crit.setFirstResult(first);
		crit.setMaxResults(count);
		return crit.list();
	}
	
	/**
	 * find a list of objects by Criteria or Criteria list and set the start row
	 * and items size
	 * 
	 * @param Order order, int first, int count, Criterion... criterion
	 * */
	public List<T> findByCriteria(Order order, int first, int count, Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getClazz());
		for (Criterion c : criterion) {
			crit.add(c);
		}

		if (order != null) {
			crit.addOrder(order);
		}
		crit.setFirstResult(first);
		crit.setMaxResults(count);
		return crit.list();
	}

	/**
	 * find list throught HQL. this method is set to make it more agile
	 * 
	 * @param String hql
	 * */
	public List<T> findByHql(String hql) {
		Session session = this.getSession();
		Query query = session.createQuery(hql);
		List<T> list = query.list();
		return list;
	}

	/**
	 * find list throught SQL. this method is set to make it more agile
	 * 
	 * @param String sql
	 * */
	public List<T> findBySql(String sql) {
		Session session = this.getSession();
		Query query = session.createSQLQuery(sql).addEntity(this.clazz);
		List<T> list = query.list();
		return list;
	}

	/**
	 * 查询并返回满足条件的记录总数
	 * 
	 * @param tableName
	 *            表的名称
	 * @param conditions
	 *            查询条件语句;
	 * @return 满足条件的记录总数
	 */
	public int getTotal(String tableName, String condition) {
		String sql =new StringBuilder().append("select count(*) from ").append(tableName).append(condition).toString();
		int total = Integer.valueOf(this.getSession().createSQLQuery(sql).uniqueResult().toString());
		return total;
	}

	/**
	 * 查询并返回满足条件的记录总数
	 * 
	 * @param tableName
	 *            表的名称
	 * @return 满足条件的记录总数
	 */
	public int getTotal(String tableName) {
		String sql = "select count(*) from " + tableName;
		int total = Integer.valueOf(this.getSession().createSQLQuery(sql).uniqueResult().toString());
		return total;
	}

	/**
	 * 清理缓存
	 */
	public void flush() {
		this.getSession().flush();
	}
}
